package org.osll.tictactoe.transport.udp.client;

import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;

import org.osll.tictactoe.Control;
import org.osll.tictactoe.CurrentMove;
import org.osll.tictactoe.Move;
import org.osll.tictactoe.Size;
import org.osll.tictactoe.Team;
import org.osll.tictactoe.transport.tcp.ApponentMoveQuery;
import org.osll.tictactoe.transport.tcp.ApponentMoveResponse;
import org.osll.tictactoe.transport.tcp.ApponentNameQuery;
import org.osll.tictactoe.transport.tcp.ApponentNameResponse;
import org.osll.tictactoe.transport.tcp.DefaultResponse;
import org.osll.tictactoe.transport.tcp.ErrorResponse;
import org.osll.tictactoe.transport.tcp.LoginQuery;
import org.osll.tictactoe.transport.tcp.LoginRejectedResponse;
import org.osll.tictactoe.transport.tcp.MoveQuery;
import org.osll.tictactoe.transport.tcp.OverQuery;
import org.osll.tictactoe.transport.tcp.OverResponse;
import org.osll.tictactoe.transport.tcp.ReflexiveTimeQuery;
import org.osll.tictactoe.transport.tcp.ReflexiveTimeResponse;
import org.osll.tictactoe.transport.tcp.SizeQuery;
import org.osll.tictactoe.transport.tcp.SizeResponse;
import org.osll.tictactoe.transport.tcp.StartedQuery;
import org.osll.tictactoe.transport.tcp.StartedResponse;
import org.osll.tictactoe.transport.udp.SocketProcessor;

public class ControlImpl extends SocketProcessor implements Control {
	
	private DatagramSocket socket = null;
	
	private String name = null;
	
	private Team team = null;
	
	public ControlImpl(String host, int port, String userName, Team team) {
		try {
			socket = new DatagramSocket();
		} catch (SocketException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		try {
			socket.connect(new InetSocketAddress(host,port));
		} catch (SocketException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		this.name = userName;
		this.team = team;
		
		LoginQuery query = new LoginQuery();
		query.setName(name);
		query.setTeam(team);
		write(socket,query);
		
		DefaultResponse r = (DefaultResponse) read(socket);
		if(r instanceof LoginRejectedResponse)	{
			socket.close();
			throw new IllegalStateException("Login was rejected");
		}
	}

	@Override
	synchronized public Move getApponentMove() {
		ApponentMoveQuery query = new ApponentMoveQuery();
		if(team == Team.X)
			query.setTeam(Team.O);
		else
			query.setTeam(Team.X);
		write(socket,query);
		DefaultResponse resp = (DefaultResponse) read(socket);
		if(resp instanceof ErrorResponse ) {
			throw ((ErrorResponse)resp).getException();
		}
		return ((ApponentMoveResponse)resp).getMove();
	}

	@Override
	synchronized public Size getSize() {
		SizeQuery query = new SizeQuery();
		write(socket,query);
		DefaultResponse resp = (DefaultResponse) read(socket);
		if(resp instanceof ErrorResponse ) {
			throw ((ErrorResponse)resp).getException();
		}
		return ((SizeResponse)resp).getSize();
	}

	@Override
	synchronized public void makeMove(int row, int col) {
		MoveQuery query = new MoveQuery();
		query.setCol(col);
		query.setRow(row);
		query.setTeam(team);
		write(socket,query);
		
		DefaultResponse resp = (DefaultResponse)read(socket);
		if(resp instanceof ErrorResponse) {
			throw ((ErrorResponse)resp).getException();
		}
	}

	@Override
	synchronized public void disconnect() {
		socket.close();
	}

	@Override
	synchronized public CurrentMove getMoveEndTime() {
		ReflexiveTimeQuery query = new ReflexiveTimeQuery();
		write(socket,query);
		return ((ReflexiveTimeResponse)read(socket)).getMove();
	}

	@Override
	synchronized public boolean isStarted() {
		StartedQuery query = new StartedQuery();
		write(socket,query);
		return ((StartedResponse)read(socket)).isStarted();
	}

	@Override
	synchronized public boolean isOver() {
		OverQuery query = new OverQuery();
		write(socket,query);
		return ((OverResponse)read(socket)).isOver();
	}

	@Override
	synchronized public String getApponentName() {
		ApponentNameQuery query = new ApponentNameQuery();
		if(team == Team.X)
			query.setTeam(Team.O);
		else 
			query.setTeam(Team.X);
		write(socket,query);
		return ((ApponentNameResponse)read(socket)).getName();
	}

}
